package com.pool.socket;

import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.pool.socket.DataType.MessageFormat;
import com.pool.socket.DataType.SocketDepict;

public class CircularBuffer {

	private byte[] buffer;
	private int cursor;
	private int availPos;
	private int validPos;

	public CircularBuffer(int size) {
		buffer = new byte[size];
		cursor = 0;
		availPos = 0;
		validPos = 0;
	}

	public byte[] removeData() { // consume data
		int len = (validPos - cursor + buffer.length) % buffer.length;
		if (len > 0) {
			byte[] data = getData(len);
			return data;
		}
		return null;
	}

	public void pushData(byte[] data, int length) { // append data to buffer
		if (length == 0)
			return; // not need to store data
		int depositLen = 0;
		if (availPos == cursor) // when this happens ,buffer should be empty
			depositLen = buffer.length;
		else
			depositLen = (cursor - availPos + buffer.length) % buffer.length;
		if (availPos >= cursor) {
			if (depositLen <= length) {
				byte[] tmp = new byte[buffer.length + length];
				System.arraycopy(buffer, 0, tmp, 0, buffer.length);
				buffer = tmp;
			}
			if ((buffer.length - availPos) < length) {
				System.arraycopy(data, 0, buffer, availPos, buffer.length
						- availPos);
				System.arraycopy(data, buffer.length - availPos, buffer, 0,
						length - buffer.length + availPos);
			} else
				System.arraycopy(data, 0, buffer, availPos, length);
		} else { // availPos < cursor
			if (depositLen <= length) {
				// System.out.println("depositLen = " + depositLen +
				// ",dataLen= "
				// + length);
				byte[] tmp = new byte[buffer.length + length];
				System.arraycopy(buffer, 0, tmp, 0, availPos);
				System.arraycopy(buffer, cursor, tmp, cursor + length,
						buffer.length - cursor);
				cursor += length;
				buffer = tmp;
			}
			System.arraycopy(data, 0, buffer, availPos, length);
		}
		availPos = (availPos + length) % buffer.length;
	}

	private byte[] getData(int size) { // will move cursor position
		int withdrawLen = (validPos - cursor + buffer.length) % buffer.length;
		if (withdrawLen < size)
			return null;
		byte[] data = new byte[size];
		if (validPos > cursor) {
			System.arraycopy(buffer, cursor, data, 0, size);
			cursor = (cursor + size) % buffer.length;
			return data;
		} else if (validPos < cursor) {
			if ((buffer.length - cursor) < size) {
				System.arraycopy(buffer, cursor, data, 0, buffer.length
						- cursor);
				System.arraycopy(buffer, 0, data, buffer.length - cursor, size
						+ cursor - buffer.length);
			} else
				System.arraycopy(buffer, cursor, data, 0, size);
			cursor = (cursor + size) % buffer.length;
			return data;
		} else
			return null; // no data in buffer
	}

	boolean packetIsValid(byte[] tmp) {
		if ((tmp[0] == Byte.valueOf("00", 16))
				&& (tmp[1] == Byte.valueOf("43", 16))
				&& (tmp[2] == Byte.valueOf("45", 16))
				&& (tmp[3] == Byte.valueOf("54", 16))
				&& (tmp[4] == Byte.valueOf("43", 16))
				&& (tmp[5] == Byte.valueOf("00", 16)))
			return true;
		else
			return false;
	}

	public int setValidPos() {
		int validateLen = (availPos - validPos + buffer.length) % buffer.length;
		//System.out.println("validateLen = " + validateLen);
		if (validateLen == 0)
			return -1;
		byte[] tmp = new byte[MessageFormat.packetHeaderLen];
		// byte[] tmp = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,};
		int offset = 0;
		byte[] packet = new byte[validateLen];
		if (validPos < availPos) {
			System.arraycopy(buffer, validPos, packet, 0, validateLen);
		} else {
			System.arraycopy(buffer, validPos, packet, 0, buffer.length
					- validPos);
			System.arraycopy(buffer, 0, packet, buffer.length - validPos,
					validateLen - buffer.length + validPos);
		}
		ByteBuffer bb = ByteBuffer.wrap(packet);
		bb.order(MessageFormat.byteOrder);
		Set<Integer> addrSet = new HashSet<Integer>();
		while (bb.remaining() >= MessageFormat.packetHeaderLen) {
			bb.get(tmp, 0, 10);
			int localAddr = bb.getInt();
			short packetLen = bb.getShort();
			if (bb.remaining() >= packetLen) {
				bb.position(offset + MessageFormat.packetHeaderLen + packetLen);
				offset += (MessageFormat.packetHeaderLen + packetLen);
				if (packetIsValid(tmp))
					addrSet.add(localAddr);
				else
					break;
			} else
				break;
		}
		if (addrSet.isEmpty() || addrSet.size() > 1) {
			availPos = validPos;// roll back data
			return -1;
		} else {
			validPos = (validPos + offset) % buffer.length;
			Iterator<Integer> it = addrSet.iterator();
			int addr = (Integer) it.next();
			//System.out.println("addr = " + addr);
			return addr;
		}
	}
}